#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QMessageBox>
#include <QJsonDocument>
#include <QJsonObject>
#include <QTextStream>
#include "serverssettingsdialog.h"
#include "ui_serverssettingsdialog.h"
#include "utils/xio.h"

ServersSettingsDialog::ServersSettingsDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ServersSettingsDialog)
{
    ui->setupUi(this);
    ui->plainTextEditJsonFormat->setPlaceholderText(
        QString("{ \"input\": \"%1\" }").arg(ServersSettingsDialog::keyword)
    );
    this->reload();
}

const QString ServersSettingsDialog::SERVERS_CONFIG_DIR = "./servers";

QString ServersSettingsDialog::compressJson(const QString& json) {
    return QJsonDocument::fromJson(json.trimmed().toUtf8()).toJson(QJsonDocument::Compact);
}

QString ServersSettingsDialog::getPrettyJson(const QString& json) {
    return QJsonDocument::fromJson(json.trimmed().toUtf8()).toJson(QJsonDocument::Indented);
}

QString ServersSettingsDialog::getFinalJson(QString jsonTemplate, QString inputStr) {
    QJsonObject obj = QJsonDocument::fromJson(jsonTemplate.toUtf8()).object();
    int keywordLength = ServersSettingsDialog::keyword.length();
    for (const QString& key : obj.keys()) {
        QString value = obj.value(key).toString();
        int idx = value.indexOf(ServersSettingsDialog::keyword);
        if (idx >= 0)
            obj[key] = value.left(idx) + inputStr + value.mid(idx + keywordLength);
    }
    return QJsonDocument(obj).toJson();
}

bool ServersSettingsDialog::parse(
    const QString& configPath, QString& serverUrl, QString& json, QString& keys
) {
    QFile file(configPath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return false;
    QTextStream in(&file);
    QStringList url_keys = in.readLine().split("\t");
    serverUrl = url_keys[0];
    keys = url_keys[1];
    QString line = in.readLine();
    json = line;
    while ((line = in.readLine()) != nullptr)
        json += "\n" + line;
    file.close();
    json = ServersSettingsDialog::compressJson(json);
    return true;
}

const QString ServersSettingsDialog::keyword = "<question>";

ServersSettingsDialog::~ServersSettingsDialog()
{
    delete ui;
}

int ServersSettingsDialog::getUrlNumber() const {
    return this->serverUrlList.length();
}

bool ServersSettingsDialog::isJsonValid(const QString& json) const {
    QString json2 = json.trimmed();
    if (json2.length() == 0)
        return false;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(json2.toUtf8());
    if (jsonDoc.isNull())
        return false;
    return true;
}

bool ServersSettingsDialog::updateJson(int index, QString json, bool display) {
    if (index < 0 || index >= this->serverUrlList.length())
        return true;
    json = json.trimmed();
    QJsonDocument jsonDoc = QJsonDocument::fromJson(json.toUtf8());
    if (jsonDoc.isNull()) {
        QMessageBox::critical(this, tr("Invalid JSON"), tr("JSON content is incomplete!"));
        return false;
    }
    if (jsonDoc.isArray()) {
        QMessageBox::warning(this, tr("Unsupported JSON"), tr("Cannot parse array now."));
        return false;
    }
    bool noKeyword = true;
    if (jsonDoc.isObject()) {
        QJsonObject jsonObj = jsonDoc.object();
        for (const QString& key : jsonObj.keys()) {
            if (key.indexOf(ServersSettingsDialog::keyword) >= 0) {
                QMessageBox::critical(this, tr("Invalid JSON"), 
                    QString("\"%1\"").arg(ServersSettingsDialog::keyword)
                        + tr(" cannot be included in a key name!")
                );
                return false;
            }
            if (jsonObj.value(key).toString().indexOf(ServersSettingsDialog::keyword) >= 0)
                noKeyword = false;
        }
    }
    if (noKeyword) {
        QMessageBox::critical(this, tr("Keyword missing"),
            tr("Missing keyword ") + QString("\"%1\"!").arg(ServersSettingsDialog::keyword));
        return false;
    }
    this->jsonList[index] = ServersSettingsDialog::compressJson(json);
    if (display) {
        ui->plainTextEditJsonFormat->setPlainText(
            ServersSettingsDialog::getPrettyJson(json)
        );
    }
    return true;
}

QString ServersSettingsDialog::getFinalKeys(const QString& keys) {
    QString res = keys.trimmed();
    if (res.length() == 0)
        return res;
    while (res.startsWith(","))
        res = res.mid(1);
    while (res.endsWith(","))
        res = res.left(res.length() - 1);
    return res;
}

QListWidgetItem* ServersSettingsDialog::createItem(const QString& text) {
    QListWidgetItem* item = new QListWidgetItem(text);
    item->setFlags(item->flags() | Qt::ItemIsEditable);
    return item;
}

void ServersSettingsDialog::create() {
    this->jsonList.push_back("");
    this->keysList.push_back("");
    this->recordServerUrl("New Server URL", true);
}

void ServersSettingsDialog::removeAt(int index) {
    if (index < 0 || index >= this->serverUrlList.length())
        return;
    this->jsonList.removeAt(index);
    this->keysList.removeAt(index);
    this->serverUrlList.removeAt(index);
    ui->listWidgetServerUrlList->setCurrentRow(-1);
    ui->listWidgetServerUrlList->takeItem(index);
    int cnt = this->serverUrlList.length();
    if (index >= cnt) {
        ui->listWidgetServerUrlList->setCurrentRow(cnt - 1);
    } else {
        ui->listWidgetServerUrlList->setCurrentRow(index);
    }
}

void ServersSettingsDialog::restore() {
    this->clear(false);
    int cnt = this->backupServerUrlList.length();
    for (int i = 0; i < cnt; ++ i) {
        this->jsonList.push_back(this->backupJsonList[i]);
        this->keysList.push_back(this->backupKeysList[i]);
        this->recordServerUrl(this->backupServerUrlList[i], false);
    }
    ui->listWidgetServerUrlList->setCurrentRow(cnt - 1);
}

void ServersSettingsDialog::clearBackup() {
    this->backupJsonList.clear();
    this->backupServerUrlList.clear();
    this->backupKeysList.clear();
}

void ServersSettingsDialog::backup(const QString& serverUrl, const QString& json, const QString& keys) {
    this->backupServerUrlList.push_back(serverUrl);
    this->backupJsonList.push_back(json);
    this->backupKeysList.push_back(keys);
}

void ServersSettingsDialog::backup() {
    this->clearBackup();
    for (int i = 0; i < this->jsonList.length(); ++ i)
        this->backup(this->serverUrlList[i], this->jsonList[i], this->keysList[i]);
}

void ServersSettingsDialog::save() {
    QDir dir;
    if (dir.exists(ServersSettingsDialog::SERVERS_CONFIG_DIR)) {
        QStringList configPaths;
        XIO::getPaths(ServersSettingsDialog::SERVERS_CONFIG_DIR, configPaths, false);
        for (const QString& path : configPaths) {
            QFile file(path);
            file.remove();
        }
    } else {
        try {
            dir.mkpath(ServersSettingsDialog::SERVERS_CONFIG_DIR);
        } catch (...) {
            QMessageBox::warning(this, tr("Invalid Server Config Directory"),
                tr("Cannot find or create server config directory ")
                    + QString("\"%1\"!").arg(ServersSettingsDialog::SERVERS_CONFIG_DIR)
            );
            return;
        }
    }
    for (int i = 0; i < this->serverUrlList.length(); ++ i) {
        XIO::writeText(
            QString("%1\t%2\n%3\n").arg(this->serverUrlList[i])
                                   .arg(this->keysList[i])
                                   .arg(this->jsonList[i]),
            QString("%1/%2.config").arg(ServersSettingsDialog::SERVERS_CONFIG_DIR).arg(i), false
        );
    }
    ui->pushButtonSave->setEnabled(false);
    ui->pushButtonReload->setEnabled(false);
    emit signal_update();
}

void ServersSettingsDialog::reload() {
    this->clear(true);
    
    QStringList configPaths;
    XIO::getPaths(ServersSettingsDialog::SERVERS_CONFIG_DIR, configPaths, false);
    QString serverUrl, json, keys;
    for (const QString& path : configPaths) {
        ServersSettingsDialog::parse(path, serverUrl, json, keys);
        this->recordServerUrl(serverUrl, false);
        this->jsonList.push_back(json);
        this->keysList.push_back(keys);
        
        this->backup(serverUrl, json, keys);
    }
    if (this->serverUrlList.length())
        ui->listWidgetServerUrlList->setCurrentRow(0);
    ui->pushButtonSave->setEnabled(false);
    ui->pushButtonReload->setEnabled(false);
}

void ServersSettingsDialog::clear(bool clearBackup) {
    ui->plainTextEditJsonFormat->clear();
    this->jsonList.clear();
    this->serverUrlList.clear();
    this->keysList.clear();
    ui->listWidgetServerUrlList->clear();
    if (clearBackup)
        this->clearBackup();
}

void ServersSettingsDialog::recordServerUrl(const QString& serverUrl, bool display) {
    this->serverUrlList.push_back(serverUrl);
    auto item = this->createItem(serverUrl);
    ui->listWidgetServerUrlList->addItem(item);
    if (display)
        ui->listWidgetServerUrlList->setCurrentItem(item);
}

void ServersSettingsDialog::closeEvent(QCloseEvent *e) {
    int choice = QMessageBox::question(this, tr("Confirm to close"), tr("Quit directly?"));
    if (choice != QMessageBox::Yes)
        e->ignore();
    this->backup();
}

void ServersSettingsDialog::on_pushButtonNew_clicked()
{
    int index = ui->listWidgetServerUrlList->currentRow();
    if (index >= 0) {
        QString json = ui->plainTextEditJsonFormat->toPlainText();
        this->keysList[index] = this->getFinalKeys(ui->lineEditTargetDataKeys->text());
        if (this->updateJson(index, json, false))
            this->create();
        return;
    }
    this->create();
}

void ServersSettingsDialog::on_pushButtonRemove_clicked()
{
    int idx = ui->listWidgetServerUrlList->currentRow();
    if (idx < 0)
        return;
    QListWidgetItem* item = ui->listWidgetServerUrlList->currentItem();
    int choice = QMessageBox::question(
        this, tr("Confirm to remove"), tr("Remove Server \"%1\"?").arg(item->text())
    );
    if (choice != QMessageBox::Yes)
        return;
    this->removeAt(idx);
    ui->pushButtonSave->setEnabled(true);
}

void ServersSettingsDialog::on_listWidgetServerUrlList_currentRowChanged(int currentRow)
{
    if (currentRow < 0) {
        ui->plainTextEditJsonFormat->setEnabled(false);
        ui->lineEditTargetDataKeys->setEnabled(false);
        ui->pushButtonRemove->setEnabled(false);
        ui->plainTextEditJsonFormat->clear();
        return;
    }
    ui->plainTextEditJsonFormat->setEnabled(true);
    ui->lineEditTargetDataKeys->setEnabled(true);
    ui->pushButtonRemove->setEnabled(true);
    ui->plainTextEditJsonFormat->setPlainText(
        ServersSettingsDialog::getPrettyJson(this->jsonList[currentRow])
    );
    ui->lineEditTargetDataKeys->setText(this->keysList[currentRow]);
}

void ServersSettingsDialog::on_pushButtonSave_clicked()
{
    int index = ui->listWidgetServerUrlList->currentRow();
    if (index >= 0 && index < this->serverUrlList.length()) {
        QString json = ui->plainTextEditJsonFormat->toPlainText();
        if (!this->updateJson(index, json, true))
            return;
        this->keysList[index] = this->getFinalKeys(ui->lineEditTargetDataKeys->text());
        ui->lineEditTargetDataKeys->setText(this->keysList[index]);
    }
    this->save();
}

void ServersSettingsDialog::on_pushButtonRestore_clicked()
{
    this->restore();
    ui->pushButtonSave->setEnabled(true);
}

void ServersSettingsDialog::on_pushButtonClose_clicked()
{
    this->close();
}

void ServersSettingsDialog::on_pushButtonReload_clicked()
{
    this->reload();
}

void ServersSettingsDialog::on_plainTextEditJsonFormat_textChanged()
{
    int index = ui->listWidgetServerUrlList->currentRow();
    if (index < 0)
        return;
    if (!ui->pushButtonSave->isEnabled()) {
        QString currentJson = ui->plainTextEditJsonFormat->toPlainText();
        if (!this->isJsonValid(currentJson))
            return;
        if (ServersSettingsDialog::compressJson(currentJson) == this->jsonList[index])
            return;
        ui->pushButtonSave->setEnabled(true);
    }
}

void ServersSettingsDialog::on_listWidgetServerUrlList_itemChanged(QListWidgetItem *item)
{
    int currentRow = ui->listWidgetServerUrlList->currentRow();
    if (currentRow < 0)
        return;
    QString text = item->text().trimmed();
    for (int i = 0; i < this->serverUrlList.length(); ++ i) {
        if (i == currentRow)
            continue;
        if (ui->listWidgetServerUrlList->item(i)->text() == text) {
            QMessageBox::critical(this, tr("Repeated Server URL"), tr("Server URL repeated!"));
            item->setText(this->serverUrlList.at(currentRow));
            return;
        }
    }
    this->serverUrlList[currentRow] = text;
    item->setText(text);
    if (!ui->pushButtonSave->isEnabled())
        ui->pushButtonSave->setEnabled(true);
}

void ServersSettingsDialog::on_lineEditTargetDataKeys_textChanged(const QString&)
{
    int index = ui->listWidgetServerUrlList->currentRow();
    if (index < 0)
        return;
    if (!ui->pushButtonSave->isEnabled()) {
        QString keys = this->getFinalKeys(ui->lineEditTargetDataKeys->text());
        if (keys == this->keysList[index])
            return;
        ui->pushButtonSave->setEnabled(true);
    }
}
